package com.jackstraw.jack_base.datasource.database.parten.aspect.aop;

import com.jackstraw.jack_base.datasource.common.entity.enums.RWEnum;
import com.jackstraw.jack_base.datasource.database.parten.aspect.annotation.MainDataBaseCut;
import com.jackstraw.jack_base.datasource.database.parten.aspect.contextholder.DataBaseRWContextHolder;
import com.jackstraw.jack_base.datasource.database.parten.exception.BadDataBaseException;
import com.jackstraw.jack_base.util.AspectUtil;
import com.jackstraw.jack_base.util.ObjectUtil;
import com.jackstraw.jack_base.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

/**
 * 数据源切面类
 */
@Slf4j
@Aspect
@Component
public class DataBaseAspect implements Ordered {

    @Value("${jack.datasource.database.rwType.read:select,query,list,get,count,page}")
    private Set<String> readSet;


    @Value("${jack.datasource.database.rwType.write:insert,save,add,get,update,edit,delete,remove}")
    private Set<String> writeSet;

    @Pointcut("@within(com.jackstraw.jack_base.datasource.database.parten.aspect.annotation.MainDataBaseCut)"
            + "|| execution(* com.jackstraw..*.dao.database..*.*(..)))")
    public void defaultPointCut() {

    }

    public Set<String> getReadSet() {
        if (ObjectUtil.ObjectUtilByLang3.isEmpty(this.readSet)) {
            this.readSet = new HashSet<>();
        }
        return readSet;
    }

    public Set<String> getWriteSet() {
        if (ObjectUtil.ObjectUtilByLang3.isEmpty(this.writeSet)) {
            this.writeSet = new HashSet<>();
        }
        return writeSet;
    }

    /**
     * 判断是否是读写方法
     *
     * @param methodName
     * @return
     */
    private Boolean isRead(String methodName) {
        return this.getReadSet().stream().filter(read -> methodName.startsWith(read)).findFirst().isPresent();
    }

    /**
     * 判断是否是读写方法
     *
     * @param methodName
     * @return
     */
    private Boolean isWrite(String methodName) {
        return this.getWriteSet().stream().filter(write -> methodName.startsWith(write)).findFirst().isPresent();
    }

    @Around("defaultPointCut()")
    public Object dbDefaultCutByAround(ProceedingJoinPoint joinPoint) {
        long currentTimeMillis = System.currentTimeMillis();
        //获取Method
        Method method = AspectUtil.getMethod(joinPoint);
        // 获得目标方法名
        String methodName = method.getName();
        //获取代理的对象
        Class aClass = AspectUtil.getClass(joinPoint);
        String className = aClass.getName();
        if (StringUtil.StringUtilsBylang3.isEmpty(className) || StringUtil.StringUtilsBylang3.isEmpty(methodName)) {
            throw new BadDataBaseException("002", "DataBase切面异常，无法获取切面类名称或切面方法名称");
        }
        log.debug("AOP环绕通知【时间戳为:{}】=========={}#{}方法执行开始", currentTimeMillis, className, methodName);
        RWEnum rwState = RWEnum.none;

        if (method.isAnnotationPresent(MainDataBaseCut.class)) {
            rwState = RWEnum.write;
        } else {
            if (isRead(methodName)) {
                rwState = RWEnum.read;
            } else if (isWrite(methodName)) {
                rwState = RWEnum.write;
            }
        }

        DataBaseRWContextHolder.set(rwState);

        Object result = null;
        try {
            //获取参数
            Object[] args = joinPoint.getArgs();
            if (args == null || args.length == 0) {
                result = joinPoint.proceed();
            } else {
                result = joinPoint.proceed(args);
            }
            return result;
        } catch (Throwable e) {
            throw new BadDataBaseException("001", "AOP环绕通知【时间戳为:{}】=========={}#{}方法执行异常", e, currentTimeMillis, className, methodName);
        } finally {
            log.debug("AOP环绕通知【时间戳为:{}】=========={}#{}方法执行结束", currentTimeMillis, className, methodName);
            DataBaseRWContextHolder.clear();
        }
    }


    @Override
    public int getOrder() {
        return 0;
    }

}
